/*
安装此模块，将
    在/proc/devices文件里生成设备(lxz_dev)及其主设备号
    在/sys/class/生成lxz_class目录
    在/sys/class/lxz_class/下生成lxz_device设备节点(文件夹)
    在/dev/下生成lxz_device设备节点，可读写
*/

#include "linux/init.h"
#include "linux/module.h"
#include "linux/device.h"
#include "linux/fs.h"
#include "linux/string.h"
#include "linux/uaccess.h"

static dev_t chrdev_id = 0;
static const char *chrdev_name = "lxz_chrdev";
static const char *sysfs_class_name = "lxz_class";
static struct class *sysfs_class = NULL;
static struct device *sysfs_device = NULL;
static const char *sysfs_device_name = "lxz_device";
static char read_buffer[64] = "data in read_buffer";
static char write_buffer[64] = {0};

int chrdev_open(struct inode *node, struct file *f)
{
    printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    return 0;
}
int chrdev_release(struct inode *node, struct file *f)
{
    printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    return 0;
}
ssize_t chrdev_read(struct file *f, char __user *buffer, size_t n, loff_t *pos)
{
    printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    size_t max_size = strlen(read_buffer);
    size_t target = n < max_size ? n : max_size;
    printk("n=%lu,target=%lu\n", n, target);

    // unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
    unsigned long remain = copy_to_user(buffer, read_buffer, target);
    if (remain)
    {
        printk("remain(%lu),target(%lu),what a bug\n", remain, target);
        return -1;
    }

    return target;
}
ssize_t chrdev_write(struct file *f, const char __user *buffer, size_t n, loff_t *pos)
{
    printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    size_t max_size = sizeof(write_buffer) - 1;
    size_t target = n < max_size ? n : max_size;
    printk("n=%lu,target=%lu\n", n, target);

    // unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
    unsigned long remain = copy_from_user(write_buffer, buffer, target);
    if (remain)
    {
        printk("remain(%lu),target(%lu),what a bug\n", remain, target);
        return -1;
    }
    printk("write_buffer=%s\n", write_buffer);

    return target;
}

static struct file_operations chrdev_fops = {
    .read = chrdev_read,
    .write = chrdev_write,
    .open = chrdev_open,
    .release = chrdev_release,
};

static int __init chrdev_fops_init(void)
{
    printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    int ret = -1;

    /*
    注册字符设备
    若major=0，则自动注册，成功并返回主设备号，失败返回负数
    若major>0，则尝试基于该主设备号进行注册，失败返回负数
    */
    ret = register_chrdev(0, chrdev_name, &chrdev_fops);
    if (ret < 0)
    {
        printk("register_chrdev error\n");
        return -1;
    }
    printk("register_chrdev succeed,ret=%d\n", ret);
    chrdev_id = MKDEV(ret, 0);

    // 创建sysfs设备类别
    sysfs_class = class_create(THIS_MODULE, sysfs_class_name);
    if (IS_ERR(sysfs_class))
    {
        printk("class_create error\n");

        unregister_chrdev(MAJOR(chrdev_id), chrdev_name);
        return -1;
    }
    printk("class_create succeed\n");

    // 创建基于设备号和sysfs设备类别的设备，将导出到/sys/class/
    sysfs_device = device_create(sysfs_class, NULL, chrdev_id, NULL, sysfs_device_name);
    if (IS_ERR(sysfs_device))
    {
        printk("device_create error\n");

        class_destroy(sysfs_class);
        unregister_chrdev(MAJOR(chrdev_id), chrdev_name);
        return -1;
    }
    printk("device_create succeed\n");

    return 0;
}

static void __exit chrdev_fops_exit(void)
{
    printk("file=%s,fun=%s,line=%d\n", __FILE__, __FUNCTION__, __LINE__);

    device_destroy(sysfs_class, chrdev_id);

    class_destroy(sysfs_class);

    unregister_chrdev(MAJOR(chrdev_id), chrdev_name);
}

module_init(chrdev_fops_init);
module_exit(chrdev_fops_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lxz");
